/*******************************************************************************
 ** Name: cmd.cpp                                                             **
 ** Description:                                                              **
 **                                                                           **
 ** Open Source Initiative (OSI) Approved 2-clause BSD License                **
 **                                                                           **
 **  Copyright (c) 2009-2010 Barry "Gian" James  <bjames@munixos.net>         **
 **  All rights reserved.                                                     **
 **                                                                           **
 **  Redistribution and use in source and binary forms, with or without       **
 **  modification, are permitted provided that the following conditions are   **
 **  met:                                                                     **
 **                                                                           **
 **  * Redistributions of source code must retain the above copyright notice, **
 **    this list of conditions and the following disclaimer.                  **
 **  * Redistributions in binary form must reproduce the above copyright      **
 **    notice, this list of conditions and the following disclaimer in the    **
 **    documentation and/or other materials provided with the distribution.   **
 **                                                                           **
 **  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS      **
 ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED **
 ** TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A           **
 ** PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT        **
 ** HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,    **
 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED  **
 ** TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR    **
 ** PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF    **
 ** LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING      **
 ** NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS        **
 ** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.              **
 **                                                                           **
 ******************************************************************************/
// Modified $Date: 2011-09-18 21:48:59 +0000 (Sun, 18 Sep 2011) $ by $Author: gian.james $
#define cmd_cpp_ID    "$Id: cmd.cpp 145 2011-09-18 21:48:59Z gian.james $"

#include <boost/tokenizer.hpp>
#include <iostream>
#include <fstream>
#include <queue>
#include "cmd.hpp"
#include "nlp.hpp"
#include "common.hpp"
#include "object.hpp"
#include "client.hpp"
#include "log.hpp"

#define PREDICATE(NAME)   \
	{ #NAME, (uint8_t)PredID::p ## NAME, CmdType::Predicate, CmdCategory::Action, 0, 0, 0, "", &Cmd::pred_ ## NAME, "", "", "",0 }
#define PREDDEF(NAME)   CmdReturn \
Cmd::pred_ ## NAME(WorldObject * dio, WorldObject * ido, Client * from, ...)
#define COMMAND(NAME)	\
	{  #NAME, (uint8_t)CmdID::c ## NAME, CmdType::Command, CmdCategory::Action, 0, 0, 0, "",&Cmd::cmd_ ## NAME, "", "", "", 0}
#define CMDDEF(NAME)	CmdReturn \
Cmd::cmd_ ## NAME(WorldObject * dio, WorldObject * ido, Client * from, ...)

static std::queue<CmdQueueNode>	cmdQueue;
static pthread_mutex_t	queueMutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t	treeMutex = PTHREAD_MUTEX_INITIALIZER;

void	Enqueue(CmdQueueNode & n)
{
	pthread_mutex_lock(&queueMutex);
	cmdQueue.push(n);
	pthread_mutex_unlock(&queueMutex);
}

Command
Cmd::commands[] =
{
    COMMAND(help),
    COMMAND(quit),
    COMMAND(stats),
    COMMAND(inventory),
    COMMAND(dossier),
    COMMAND(skill),
    COMMAND(motd)
};

Command
Cmd::predicates[] =
{
    PREDICATE(put),
    PREDICATE(take),
    PREDICATE(examine),
    PREDICATE(look),
    PREDICATE(say),
    PREDICATE(tell),
    PREDICATE(shout)

//    {"",0,0,NULL}
};
// I hate this, but these have to be updated if you add or remove any
// commands or predicates
#define NUM_PREDICATES	7
#define NUM_COMMANDS	7

//Command::Command()
//{
//	synonyms = new std::vector<std::string>();
//}

int
Cmd::visit(WorldObject * o)
{
	return 0;
}

int
Cmd::BuildCmdTree()
{
	int id = 0;
	for(int x = 0; x < NUM_PREDICATES; x++) {
		predicates[x].id = ++id;
		cmdTree.insert(predicates[x]);
	}
	for(int x = 0; x < NUM_COMMANDS; x++) {
		commands[x].id = ++id;
		cmdTree.insert(commands[x]);
	}

	return cmdTree.size();
}

bool
Cmd::IsPronoun(const char * a)
{


	return false;
}

bool
Cmd::IsArticle(const char * a)
{
    const char * ptr = detr[0];
    std::cout << "ptr=" << ptr << " a=" << a << std::endl;
    while(ptr) {
       if (!strcmp(a,ptr))
           return true;
       ptr += sizeof(char*);
    }
    return false;
}

bool
Cmd::IsPrep(const char * p)
{
    const char * ptr = prep[0];
    while(ptr) {
        if (!strcmp(ptr,p))
            return true;
        ptr += sizeof (char*);
    }
    return false;
}

int
Cmd::ProcessQueue()
{
	bool	bAlreadyUnlocked = false;

	pthread_mutex_lock(&queueMutex);
	if (!cmdQueue.empty())
	{
		CmdQueueNode  n = cmdQueue.front();
		cmdQueue.pop();
		pthread_mutex_unlock(&queueMutex);
		bAlreadyUnlocked = true;

		return parse(n.cmd,n.from);
	}
	if (bAlreadyUnlocked == false)
		pthread_mutex_unlock(&queueMutex);
	return 0;
}

// I am currently looking at two options for parse(). A simple, naive
// tokenizer with semantic parsing on the back-end, or building a
// grammar and actually parsing the command using a boost::qi::spirit
// TODO
int
Cmd::parse(std::string & cmdstring, Client * from)
{
	typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
	boost::char_separator<char> sep(" \t");
	tokenizer tokens(cmdstring,sep);
	tokenizer::iterator i = tokens.begin();
	std::string cmd = (*i);
	int idx = 0;
	for(i = tokens.begin(); i != tokens.end(); ++i)
	{
		argv[idx++] = (char*)(*i).data();
	}
	argc = ++idx;

	Command c;
	c.cmd = cmd;

	// Guard global cmdTree
	pthread_mutex_lock(&treeMutex);
	splaySet::iterator ic = cmdTree.find(c);
	pthread_mutex_unlock(&treeMutex);


	Command  cp = (*ic);
	if (cp.type == CmdType::Predicate)
	{
		// nlp processing
		// search objects in 'from's room/area first
		Room * r = from->Player()->Agent()->Location();
		// search dio first

	} else if (cp.type == CmdType::Command)
	{
		// cmd args1 argsN
	}
	// Fill in our WorldObject * dio and ido
	switch(argc)
	{
	case 0:
//		cp.*cmdFunc();
		break;
	case 1:
		break;
	case 2:
		break;
	case 3:
		break;
	case 4:
		break;
	case 5:
		break;
	case 6:
		break;
	case 7:
		break;
	}

    return argc;
}


// Command definitions
// INFO TODO Should a socket fd be passed in to cmd funcs or maybe the
// entire client?
CMDDEF(motd)
{
	CmdReturn ret;
	char buff[256];

    std::ifstream	motdfs(MOTD);
    if (motdfs.good()) {
    	while(!motdfs.eof()) {
    		motdfs.getline(buff,256);
    		// TODO
  //  		Socket::WriteLine(s,buff);
    	}
    	ret.ret = CmdReturnType::Ok;
    } else
    	ret.ret = CmdReturnType::Error;
    motdfs.close();

	return ret;
}

CMDDEF(skill)
{
    CmdReturn   ret;
    // TODO
    return ret;
}

CMDDEF(dossier)
{
    CmdReturn   ret;

    return ret;
}

CMDDEF(stats)
{
    CmdReturn   ret;

    return ret;
}

CMDDEF(news)
{
    CmdReturn   ret;

    return ret;
}

CMDDEF(quit)
{
    CmdReturn   ret;

    return ret;
}

CMDDEF(help)
{
    CmdReturn   ret;

    return ret;
}

PREDDEF(shout)
{
    CmdReturn   ret;

    return ret;
}

// /tell <player> <message> [dio subj]
// tell <message> to <player> [subj to ido]
// /tell <who> <what>
// 1 argument: msg
PREDDEF(tell)
{
	DBGFUNC();
    CmdReturn   ret;
    Client * c = dynamic_cast<Client*>(dio);
    if (c == NULL) {
    	// Try the other way
    	c = dynamic_cast<Client*>(ido);
    	if (c == NULL) {
    		ret.ret = CmdReturnType::Error;
    		return ret;
    	}
    }

    va_list ap;
    va_start(ap,from);
    char * msg = va_arg(ap,char *);
    va_end(ap);

    ret.ret = (CmdReturnType)c->Tell(from->Player()->Agent()->Codename(),msg);

    return ret;
}

PREDDEF(say)
{
    CmdReturn   ret;

    return ret;
}

PREDDEF(look)
{
    CmdReturn   ret;

    return ret;
}

PREDDEF(examine)
{
    CmdReturn   ret;

    return ret;
}

PREDDEF(put)
{
    CmdReturn   ret;

    return ret;
}

CMDDEF(buy)
{
    CmdReturn   ret;

    return ret;
}

PREDDEF(take)
{
    CmdReturn   ret;

    return ret;
}

CMDDEF(inventory)
{
    CmdReturn   ret;

    return ret;
}



// Help definitions
void
Cmd::help(std::string)
{
    
}


// Constructors, destructors
Cmd::Cmd()
{
    **argv = '\0';
    argc = 0;
}

Cmd::Cmd(const Cmd& orig)
{
}

Cmd::~Cmd()
{
	if (argc) {
		for (int x = 0; x < argc; x++)
		{
			delete [] argv[x];
			delete [] argv;
		}
	}
	pthread_mutex_destroy(&queueMutex);
	pthread_mutex_destroy(&treeMutex);
}

